﻿//Copyright (c) 2009, Andreas Grünwald
//All rights reserved.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;

#if !SILVERLIGHT       
using yaTDL.Core.Properties;
using System.Threading;
#endif

namespace yaTDL.Core.IO
{
    /// <summary>
    /// Provides basic functionality for classes that want to save and open files.
    /// Every class that wants to do so, should inherit from this class and implement the abstract properties and methods.
    /// </summary>    
#if !SILVERLIGHT
    public abstract class FileHandler:IDisposable
#else
    public abstract class Fileaccess
#endif
    {

        public FileHandler() 
        {
            EnableWriting = true;
        }

#if !SILVERLIGHT
        public FileHandler(bool enableMonitoring)
        {
            EnableWriting = true;

            setMonitoring(enableMonitoring);
            defaultFile_FileSystemWatcher.EnableRaisingEvents = true;
        }
#endif


        /// <summary>
        /// Returns the default file name for file that will be used by OpenProjects() and SaveProjects() if no other filename 
        /// has been specified. The file should be located in defaultDirectory.
        /// </summary>
        public abstract string DefaultFileName{ get; }

        

        /// <summary>
        /// Saves the collection of projects to a file at the indicated location
        /// </summary>
        /// <param name="projects">The Collection that contains all Projects that will be written to the file.</param>
        /// <param name="tasks">The Collection of Tasks that will be stored in the file.</param>
        /// <param name="changes">The Collection of all Changes that will be stored in the file.</param>
        /// <param name="fileName">
        /// .net: Specifies the full filename (including the path) of the file the Projects will be written to.
        /// Silverlight: Specifies the filename in IsolatedStorage of the file the Projects will be written to.
        /// </param>
        public abstract void SaveData(Collection<Project> projects, Collection<Task> tasks, Collection<Change> changes, string fileName);

        /// <summary>
        /// Reads all Projects from the specified file.
        /// </summary>
        /// <param name="fileName">The full, absolute path of the file to be read.</param>
        /// <returns>Returns a Collection of all Projects found in the File.</returns>
        public abstract Collection<Project> GetProjects(string fileName);

        /// <summary>
        /// Reads all Tasks from the specified file.
        /// </summary>
        /// <param name="fileName">The full, absolute path of the file to be read.</param>
        /// <returns>Returns a Collection of all Tasks found in the file.</returns>
        public abstract Collection<Task> GetTasks(string fileName);

        /// <summary>
        /// Reads all Changes from the specified file.
        /// </summary>
        /// <param name="fileName">The full, absolute path of the file to be read.</param>
        /// <returns>Returns a Collection of all Chnages found in the file.</returns>
        public abstract Collection<Change> GetChanges(string fileName);

        /// <summary>
        /// Specifies whether data can be written to file.
        /// </summary>
        public bool EnableWriting { get;set;}


        /// <summary>
        /// Saves the indicated Collection of Projects to the default file.
        /// </summary>
        /// <param name="projects">The Collection that contains all Projects that will be written to the file.</param>
        public void SaveData(Collection<Project> projects, Collection<Task> tasks, Collection<Change> changes)
        {
            SaveData(projects, tasks, changes, DefaultFileName);
        }        

        /// <summary>
        /// Reads the default file and adds new Projects, Tasks, and Changes.
        /// Existing item will be updated. Items that are not present in the file but stored inside the program will remain there.
        /// </summary>
        public void OpenFile()
        {
            OpenFile(DefaultFileName);
        }

        /// <summary>
        /// Reads the default file and adds new Projects, Tasks, and Changes.
        /// Existing item will be updated. Items that are not present in the file but stored inside the program will remain there.
        /// </summary>
        /// <param name="fileName">
        /// .net: The full, absolute path of the file to be read.
        /// Silverlight: The name of the file in IsolatedStorage
        /// </param>
        public void OpenFile(string fileName)
        {
            Collection<Change> changes = GetChanges(fileName);
            foreach (Change c in changes)
            {
                TaskStore.AddChange(c);
            }

            Collection<Project> projects = GetProjects(fileName);            
            foreach (Project p in projects)
            {
                TaskStore.AddProject(p);
            }

            Collection<Task> tasks = GetTasks(fileName);
            foreach (Task t in tasks)
            {
                TaskStore.AddTask(t);
            }

        }


        /// <summary>
        /// Reads all Projects from the default file.
        /// </summary>       
        /// <returns>Returns a Collection of all Projects found in the file.</returns>
        public Collection<Project> GetProjects()
        {
            return GetProjects(DefaultFileName);
        }

        /// <summary>
        /// Reads all Tasks from the default file.
        /// </summary>
        /// <returns>Returns a Collection of all Tasks found in the file.</returns>
        public Collection<Task> GetTasks()
        {
            return GetTasks(DefaultFileName);
        }

        /// <summary>
        /// Reads all Changes from the default file.
        /// </summary>
        /// <returns>Returns a Collection of all Changes found in the file.</returns>
        public Collection<Change> GetChanges()
        {
            return GetChanges(DefaultFileName);
        }

#if !SILVERLIGHT
        
        /// <summary>
        /// Returns the default directory for saving a file. This property is read only.
        /// </summary>
        /// <remarks>The default directory is %AppData%\yaTDL\.</remarks>        
        public static string DefaultDirectory
        { 
#if DEBUG
            get { return Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\yaTDL_Debug"; }
#else
            get { return Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\yaTDL"; }
#endif
        }



        /// <summary>
        /// Gets the DateTime of the specified file's last edit.
        /// </summary>
        /// <param name="fileName">The file to be read.</param>
        /// <returns>Returns a DateTime indicating when the specified file has been editet the last time.</returns>
        public abstract DateTime GetDateOfLastEdit(string fileName);


        /// <summary>
        /// Updates all Projects, Tasks and Changes with data from the default file.
        /// New Items will be added, existing items will be overwritten with data from the file. Items that are not present in the file will be removed from the program.
        /// </summary>
        public void UpdateFromFile()
        {
            UpdateFromFile(DefaultFileName);
        }

        /// <summary>
        /// Updates all Projects, Tasks and Changes with data from the specified file.
        /// New Items will be added, existing items will be overwritten with data from the file. Items that are not present in the file will be removed from the program.
        /// </summary>
        /// <param name="fileName">The full, absolute path of the file to read.</param>
        public void UpdateFromFile(string fileName)
        {
            EnableWriting = false;
            
            try
            {
                OpenFile(fileName);
            }
            catch (Exception)
            {                
                UpdateFromFile(fileName);
                return;
            }


            Collection<Project> _projects = TaskStore.GetProjects();
            Dictionary<String, Project> projectsDict = GetProjects(fileName).ToDictionary<Project, string>(x => x.Id);

            Collection<Task> _tasks = TaskStore.GetTasks();
            Dictionary<String, Task> tasksDict = GetTasks(fileName).ToDictionary<Task, String>(x => x.Id);



            int maxIndex = _projects.Count - 1;
            for (int i = 0; i <= maxIndex; i++)
            {
                if (!projectsDict.ContainsKey(_projects[i].Id))
                {
                    if(_projects[i].LastEdited < GetDateOfLastEdit(fileName))
                    {
                        TaskStore.RemoveProject(_projects[i].Id);
                        i -= 1;
                        maxIndex -= 1;
                    }
                }
            }

            maxIndex = _tasks.Count - 1;
            for (int i = 0; i <= maxIndex; i++)
            {
                if (!tasksDict.ContainsKey(_tasks[i].Id))
                {
                    if(_tasks[i].LastEdited < GetDateOfLastEdit(fileName))
                    {
                        TaskStore.RemoveTask(_tasks[i].Id);
                        i -= 1;
                        maxIndex -= 1;
                    }
                }
            }



            //for (int i = 0; i < TaskStore.GetAllProjects().Count; i++)
            //{
            //    Project TaskStore_CurrentProject = TaskStore.GetAllProjects()[i];
            //    Project Projects_CurrentProject = projectsDict[TaskStore.GetAllProjects()[i].Id];
            //    maxIndex=TaskStore_CurrentProject.GetTasks().Count - 1;

            //    for (int i2 = 0; i2 <= maxIndex; i2++)
            //    {
            //        string currentTaskId = TaskStore_CurrentProject.GetTasks()[i2].Id;
            //        if (!Projects_CurrentProject.TaskExists(currentTaskId))
            //        {
            //            if (TaskStore_CurrentProject.GetTask(currentTaskId).LastEdited < GetDateOfLastEdit(fileName))
            //            {
            //                TaskStore_CurrentProject.DeleteTask(currentTaskId);
            //                i2 -= 1;
            //                maxIndex -= 1;
            //            }
            //        }
            //    }
            //}


            EnableWriting = true;

        }


        /// <summary>
        /// The FileSystemWatcher that monitors the directory that contains the defualt data file.
        /// </summary>
        protected FileSystemWatcher defaultFile_FileSystemWatcher; 

        /// <summary>
        /// Activated or Deactivates the monitoring of the default data file.
        /// </summary>
        /// <param name="IsEnabled"></param>
        private void setMonitoring(bool IsEnabled)
        {
            if (!Directory.Exists(DefaultDirectory)) { Directory.CreateDirectory(DefaultDirectory); }
            defaultFile_FileSystemWatcher = new FileSystemWatcher(Path.GetDirectoryName(DefaultFileName));

            if (IsEnabled)
            {
                defaultFile_FileSystemWatcher.Changed += defaultFile_FileSystemWatcher_Changed;
            }
            else
            {
                defaultFile_FileSystemWatcher.Changed -= defaultFile_FileSystemWatcher_Changed;
            }
        }


        /// <summary>
        /// Reads the default data file after the FileSystemWatchter has raised a Changed event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void defaultFile_FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            if (e.Name == Path.GetFileName(DefaultFileName))
            {
                UpdateFromFile();
            }
        }


        #region IDisposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
            return;
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                defaultFile_FileSystemWatcher.Dispose();
            }

        }

        ~FileHandler()
        { Dispose(false); }

        #endregion
#endif
    }
}
